#!/usr/bin/env ruby
require 'fileutils';
require 'nokogiri'
require 'redcarpet'
require 'rouge'
require 'rouge/plugins/redcarpet'
require 'ostruct'
require 'erb'
require 'date'
require 'yaml'
class HTML < Redcarpet::Render::HTML; include Rouge::Plugins::Redcarpet; end
class String; def color(code); "\e[#{code}m#{self}\e[0m"; end; end;
class ErbalT < OpenStruct; def render(template); ERB.new(template).result(binding); end; end

class H#elpers
  def self.datefmt(d);      d.strftime("%Y / %b %e"); end
  def self.monthfmt_let(d); d.strftime("%Y / %b"); end
  def self.monthfmt_num(d); d.strftime("%Y-%m"); end
  def self.nav_active(l_sect, c_sect); l_sect == c_sect ? "active": ""; end

  def self.run_template(template, params)
    #ERB.new(part).result_with_hash(parms)
    et = ErbalT.new(params)
    et.render(template)
  end

  def self.get_partials()
    return $partials if $partials
    $partials = Dir["./src/tmpl/*.erb.html"]
      .select { |p| p.match(/partial_.+/) }
      .map { |p|
        partial_name = p.split("/")[-1].split(".")[0].gsub("partial_", "")
        tmpl = File.read(p)
        [partial_name.to_sym, tmpl]
      }.to_h
  end
  def self.partial(partial, context = {})
    part, parms = H.get_partials()[partial], U.symbolify_keys(context)
    self.run_template(part, parms)
  end
end

class U#tilities
  def self.mkdirp_for_file(file); d = File.dirname(file); unless File.directory?(d); FileUtils.mkdir_p(d); end; end
  def self.symbolify_keys(hash); hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}; end
  def self.read_file_yaml_and_md(file)
    sep_c, fem_content, md_content = 0, [], []
    File.readlines(file).each do |l|
      if l.strip == "---" then; sep_c += 1; next; end
      (sep_c > 1 ? md_content : fem_content).push(l)
    end
    fem_content, md_content = fem_content.join("\n").strip, md_content.join("").strip
    markdown = Redcarpet::Markdown.new(HTML.new, :fenced_code_blocks => true, :tables => true )
    y, m = YAML.load(
      fem_content,
      permitted_classes: [Date, Time]
    ), markdown.render(md_content)
    begin
      m = Nokogiri::HTML(m)
      m.search("//a").each do |a|
        a['target'] = '_blank' if (a.attribute("href").content =~ /^http(s?)/)
      end
    rescue; puts "Error Nokogiri processing #{file}"; end;
    { :md => m, :yaml => y, :file => file }
  end
  def self.compile_template(parms)
    if parms[:source_content_file]
      yaml, md = U.read_file_yaml_and_md(parms[:source_content_file]).values_at(:yaml, :md) 
      parms[:content] = md; parms.merge!(yaml)
    end
    parms = U.symbolify_keys(parms)
    U.mkdirp_for_file(parms[:dest_file])
    File.write(
      parms[:dest_file], H.run_template(File.read(parms[:template_file]), parms)
    )
  end
  def self.get_listing_items_for_section(section)
    listing_items = Dir["./src/#{section}/*.md"].map { |f|
      U.read_file_yaml_and_md(f)
    }.map { |f|
      if f[:yaml] && f[:yaml]['url'] != nil
        f[:url] = f[:yaml]['url']
      else
        f[:target] = "_blank"
        f[:url] = U.post_raw_filename_to_target_filename(f[:file], f[:title], true); f
      end
      f
    }.sort { |a,b| 
      if b[:yaml]['sort_index'] ||  b[:yaml]['sort_index']
        a[:yaml]['sort_index'] <=> b[:yaml]['sort_index']
      else
        b[:yaml]['date'] <=> a[:yaml]['date']
      end
    }
  end
  def self.post_raw_filename_to_target_filename(f, section, as_url_link)
    "#{section}/" + f.downcase
      .gsub(/\d{4}-\d{2}-\d{2}-/, '')
      .gsub("./src/#{section}", '')
      .gsub(/\.md$/, as_url_link ? '' : '/index.html')
  end
end

class Task
  SECTIONS = %w[notes projects]

  def self.build_about_page
    U.compile_template(
      section: "about",
      dest_file: 'dist/' + "index.html",
      template_file: "./src/tmpl/layout_about.erb.html",
      source_content_file: "./src/about.md"
    )
  end
  def self.build_post_pages
    SECTIONS.each do |section|
      Dir["./src/#{section}/*.md"].each do |f|
        U.compile_template(
          section: section,
          dest_file: 'dist/' + U.post_raw_filename_to_target_filename(f, section, false),
          template_file: "./src/tmpl/layout_post.erb.html",
          source_content_file: f
        )
      end
    end
  end
  def self.build_listing_pages
    notes_section = "notes"
    notes_listing_items = U.get_listing_items_for_section(notes_section)
    dest_fold = "#{notes_section}/"
    U.compile_template(
      section: notes_section,
      title: notes_section.capitalize,
      dest_file: "./dist/#{notes_section}/index.html",
      template_file: "./src/tmpl/layout_listing_page.erb.html",
      listing_items: notes_listing_items
    )

    project_listing_items = U.get_listing_items_for_section("projects").reject { |item| item[:yaml]["archived"] }
    projects_section = "projects"
    dest_fold = "#{projects_section}/"
    U.compile_template(
      section: projects_section,
      title: projects_section.capitalize,
      dest_file: "./dist/#{projects_section}/index.html",
      template_file: "./src/tmpl/layout_listing_page.erb.html",
      listing_items: project_listing_items
    )

    archived_section = "archived"
    archived_listing_items = U.get_listing_items_for_section("projects").select { |item| item[:yaml]["archived"] }
    dest_fold = "#{archived_section}/"
    U.compile_template(
      section: archived_section,
      title: archived_section.capitalize,
      dest_file: "./dist/#{archived_section}/index.html",
      template_file: "./src/tmpl/layout_listing_page.erb.html",
      listing_items: archived_listing_items
    )
  end
  def self.clean; %x[rm -rf ./dist]; end
  def self.build_sass_to_css; %x[mkdir -p dist && sassc src/tmpl/scss/all.scss > dist/all.css]; end
  def self.copy_assets; FileUtils.cp_r "./src/assets/.", './dist/assets'; end
  def self.copy_root; FileUtils.cp_r "./src/assets/_root/.", './dist/'; end

  ############################################################################
  def self.main
    puts "======"
    %w[
      clean build_sass_to_css copy_assets copy_root build_post_pages 
      build_listing_pages build_about_page
    ].each {|fn|
      print fn.color(34);
      t1 = Time.now; Task.send(fn); t2 = Time.now; delta = t2 - t1;
      print " executed in #{(delta.to_s + "s").color(36)}\n";
    }
    puts "======"
  end
end

Task.main